#include <iostream>
#include <zlib/eventsystem/I_EventSystem.h>
#include <unistd.h>

using namespace std;

extern EventProcessor eventProcessor;

int ET_TEST = ET_CALL;
off_t offset = 0;

class TestCont : public Continuation
{
public:
	TestCont() : number(0)
	{
		cout << "construct function" << endl;
		SET_HANDLER(&TestCont::mainHandler);
	}

	int mainHandler(int event, void *data)
	{
		Event *e = (Event *) data;
		ink_atomic_increment(&number, 1);
		//number++;
		if(number >= 10000000)
		{
			e->cancle_action();
			delete this;
			return EVENT_DONE;
		}

		if(event == EVENT_IMMDIATE)
		{
			cout << this_ethread() << " immdate " << dec << number << endl;
			eventProcessor.schedule_imm(this, ET_CALL);
		}else if(event == EVENT_INTERVAL)
		{
			cout << this_ethread() << " interval " << dec << number << endl;
			eventProcessor.schedule_at(this, 3, ET_CALL);
		}

		return EVENT_CONT;
	}

	int number;
};


class TestCont1 : public Continuation
{
public:
	TestCont1() : number(0)
	{
		cout << "construct function" << endl;
		SET_HANDLER(&TestCont1::mainHandler);
	}

	int mainHandler(int event, void *data)
	{
		Event *e = (Event *) data;
		number++;
		if(number >= 20)
		{
			e->cancle_action();
			delete this;
			return EVENT_DONE;
		}

		if(event == EVENT_IMMDIATE)
		{
			cout << this_ethread() << " immdate " << dec << number << endl;
			eventProcessor.schedule_imm(this, ET_TEST);
		}else if(event == EVENT_INTERVAL)
		{
			cout << this_ethread() << " interval " << dec << number << endl;
			eventProcessor.schedule_at(this, 3, ET_TEST);
		}

		return EVENT_CONT;
	}

	int number;
};

class TestCont2 : public Continuation
{
public:
	TestCont2()
	{
		cout << "TestCont2: construct function" << endl;
		SET_HANDLER(&TestCont2::mainHandler);
	}

	int mainHandler(int event, void *data)
	{
		cout << "main handler" << endl;
		//get private data
		int *number = (int *)ETHREAD_GET_PRIVATE(this_ethread(), offset);
		cout << "data:" << *number << endl;
		sleep(1);
		eventProcessor.schedule_imm(this, ET_CALL);
	}
};


int main()
{
	TestCont *t = new TestCont;
	t->mutex = new_ProxyMutex();
	TestCont *t2 = new TestCont;
	t2->mutex = new_ProxyMutex();

	TestCont1 *t3 = new TestCont1();
	TestCont1 *t4 = new TestCont1();
	t3->mutex = new_ProxyMutex();
	t4->mutex = new_ProxyMutex();

	eventProcessor.start(4, 1);
	ET_TEST = eventProcessor.spawn_event_threads(10, "ET_TEST");
	eventProcessor.schedule_at(t2, 3, ET_CALL);
	eventProcessor.schedule_imm(t, ET_CALL);
	eventProcessor.schedule_at(t3, 5000000, ET_TEST);
	eventProcessor.schedule_imm(t4, ET_TEST);
	
	/***** testing thread private data spec **************\
	eventProcessor.start(20);
	offset = eventProcessor.allocate_thread_spec(sizeof(int), ET_CALL);
	int et_call = eventProcessor.n_threads_for_type[ET_CALL];
	for(int i = 0; i < et_call; i++)
	{
		EThread *t = eventProcessor.event_threads[ET_CALL][i];
		int *data = (int *)ETHREAD_GET_PRIVATE(t, offset);
		new(data)int;
		*data = i;
	}

	TestCont2 t2;
	t2.mutex = new_ProxyMutex();
	eventProcessor.schedule_imm(&t2, ET_CALL);
	\*****************************************************/

	while(true)
	{
		sleep(10);
	}
	return 0;
}
